using System;
using System.Collections.Generic;
using System.IO;

namespace Transforms.Core
{
    public class Descripter
    {
        private readonly List<FileItem> _files;
        private readonly List<string> _arxFiles;
        // private string _message; TODO: add internal messages

        public Identity OutIdentity { get; private set; }

        public void AddArx(string name)
        {
            _arxFiles.Add(name);
        }

        public int FileCount
        {
            get { return _files.Count; }
        }

        public int ArxCount
        {
            get { return _arxFiles.Count; }
        }

        public IEnumerable<string> GetArxEnumerator()
        {
            foreach (var item in _arxFiles)
                yield return item;
        }

        public Descripter()
        {
            _files = new List<FileItem>();
            _arxFiles = new List<string>();
        }

        public Descripter(byte[] data, Action<string> addNameAction)
            : this()
        {
            if (data == null || data.Length < 48)
                throw new WrongHeaderException();

            var stream = new MemoryStream(data);

            var key = new byte[32];
            var iv = new byte[16];
            stream.Seek(0, SeekOrigin.Begin);
            stream.Read(key, 0, 32);
            stream.Read(iv, 0, 16);
            var identity = new Identity(key, iv);

            var header = new byte[stream.Length - 32 - 16];
            stream.Read(header, 0, header.Length);
            try
            {
                stream = new MemoryStream(DataUtils.DecompressAndDecript(header, identity));

                var count = stream.HReadInt();
                _files.Clear();
                for (var i = 0; i < count; i++)
                {
                    var item = new FileItem { Length = stream.HReadInt(), Name = stream.HReadName() };
                    _files.Add(item);

                    if (addNameAction != null)
                        addNameAction(item.Name);
                }

                count = stream.HReadInt();
                _arxFiles.Clear();
                for (var i = 0; i < count; i++)
                    _arxFiles.Add(stream.HReadName());
            }
            catch (Exception)
            {
                throw new WrongHeaderException();
            }

            OutIdentity = identity;
        }

        public IEnumerable<FileItem> FilesEnumerator()
        {
            foreach (var item in _files)
                yield return item;
        }

        private byte[] ComposeHeader(Identity identity)
        {
            var stream = new MemoryStream();
            stream.HWrite(_files.Count);
            foreach (var item in _files)
            {
                stream.HWrite(item.Length);
                stream.HWrite(item.Name);
            }

            stream.HWrite(_arxFiles.Count);
            foreach (var name in _arxFiles)
                stream.HWrite(name);

            return DataUtils.CompressAndCript(stream.ToArray(), identity);
        }

        public void SaveToFile(string fileName, Identity identity)
        {
            using (var stream = new MemoryStream())
            {
                // TODO: real save
                stream.HWrite(identity.Compose(null));
                stream.HWrite(ComposeHeader(identity));
                stream.HSave(fileName);
            }
        }

        public byte[] LoadSources(DataSourceList list)
        {
            var stream = new MemoryStream();
            foreach (var pair in list)
            {
                var bytes = pair.Value.GetData();
                stream.HWrite(bytes);
                _files.Add(new FileItem { Name = pair.Key, Length = bytes.Length });
            }
            return stream.ToArray();
        }

        public bool CheckContent()
        {
            throw new NotImplementedException();
        }
    }
}